﻿using System;
using Core.DataAccess.Tools.Interfaces;
using Microsoft.Practices.Unity;
using NHibernate;

namespace Core.DataAccess.Tools.Implementations
{
    public class Conversation : IConversation, IDisposable
    {
        private readonly ISessionFactoryManager _sessionManager;

        private ISession session;

        public ISession Session
        {
            get { return session; }
            private set { session = value; }
        }

        [InjectionConstructor]
        public Conversation(ISessionFactoryManager sessionManager)
        {
            _sessionManager = sessionManager;
        }


        /// <summary>
        /// Starts this instance.
        /// </summary>
        public void Start()
        {
            if (Session != null && (Session.IsOpen || Session.IsConnected))
            {
                Abort();
            }
            Session = _sessionManager.GetSessionFactory().OpenSession();
            Resume();
        }

        /// <summary>
        /// Pauses this instance.
        /// </summary>
        public void Pause()
        {
            Commit(Session);
        }

        /// <summary>
        /// Resumes this instance.
        /// </summary>
        public void Resume()
        {
            if (Session == null)
                Start();

            if (Session == null)
                throw new AccessViolationException("Session could not be created");

            if (!Session.IsConnected)
            {
                Session.Reconnect();
            }
            Session.BeginTransaction();
        }

        /// <summary>
        /// Ends this instance.
        /// </summary>
        public void End()
        {
            if (Session == null || !Session.IsOpen) return;

            FlushAndCommit(Session);
            Session.Close();
        }

        /// <summary>
        /// Aborts this instance.
        /// </summary>
        public void Abort()
        {
            if (Session == null || !Session.IsOpen) return;

            if (Session.Transaction != null && Session.Transaction.IsActive)
            {
                Session.Transaction.Rollback();
            }
            Session.Close();
        }

        /// <summary>
        /// Commits the specified session.
        /// </summary>
        /// <param name="session">The session.</param>
        private static void Commit(ISession session)
        {
            if (session.Transaction != null && session.Transaction.IsActive)
            {
                session.Transaction.Commit();
            }
        }

        /// <summary>
        /// Flushes the and commit.
        /// </summary>
        /// <param name="session">The session.</param>
        private static void FlushAndCommit(ISession session)
        {
            if (session.Transaction == null || !session.Transaction.IsActive) return;

            session.Flush();
            session.Transaction.Commit();
        }

        #region Implementation of IDisposable

        public void Dispose()
        {
            End();
        }

        #endregion
    }
}


